﻿/*********************************************************************************
 *Author:         OnClick
 *Version:        1.0
 *UnityVersion:   2018.3.1f1
 *Date:           2019-03-31
 *Description:    IFramework
 *History:        2018.11--
*********************************************************************************/
using UnityEngine;
using UnityEngine.SceneManagement;
namespace IFramework
{
    public class SceneManager:Singleton<SceneManager>,ISceneManager
	{
        private CycArray<Scene> scenes;
        private LoadSceneEventArg arg ;
        private LockParam lockParam = new LockParam();
        public const string EmptySceneName="Empty";
        public Scene EmptyScene;
        private AsyncOperation asyncOp;
        public int TotalSceneCount { get { return UnityEngine.SceneManagement.SceneManager.sceneCountInBuildSettings; } }
        public int loadedSceneCount { get { return UnityEngine.SceneManagement.SceneManager.sceneCount; } }
        public Scene CurrentScene { get { return UnityEngine.SceneManagement.SceneManager.GetActiveScene(); } }

        private void ActiveSceneChange(Scene oldScene, Scene newSene)
        {
            using (LockWait wait = new LockWait(ref lockParam))
            {
                arg.UnLoadedScene = oldScene;
                arg.LoadedScene = newSene;
                arg.EveType = SceneEventType.SceneChange;
                ObserveManager.Instance.Notify<SceneManager>(arg, 1);
            }
        }
        private void SceneUnLoaded(Scene scene)
        {
            using (LockWait wait = new LockWait(ref lockParam))
            {
                scenes.Remove(scene);
                arg.EveType = SceneEventType.SceneUnLoaded;
                arg.UnLoadedScene = scene;
                ObserveManager.Instance.Notify<SceneManager>(arg, 0);
            }
        }
        private void SceneLoaded(Scene scene, LoadSceneMode mode)
        {
            using (LockWait wait = new LockWait(ref lockParam))
            {
                scenes.Add(scene);
                arg.EveType = SceneEventType.SceneLoaded;
                arg.LoadMode = mode;
                arg.LoadedScene = scene;
                ObserveManager.Instance.Notify<SceneManager>(arg, 0);
            }
        }

        public override void OnSingletonInit()
        {
            arg = new LoadSceneEventArg();
            scenes = new CycArray<Scene>();
            UnityEngine.SceneManagement.SceneManager.sceneLoaded += SceneLoaded;
            UnityEngine.SceneManagement.SceneManager.sceneUnloaded += SceneUnLoaded;
            UnityEngine.SceneManagement.SceneManager.activeSceneChanged += ActiveSceneChange;
        }
        public override void Dispose()
        {
            base.Dispose();
            UnityEngine.SceneManagement.SceneManager.sceneLoaded -= SceneLoaded;
            UnityEngine.SceneManagement.SceneManager.sceneUnloaded -= SceneUnLoaded;
            UnityEngine.SceneManagement.SceneManager.activeSceneChanged -= ActiveSceneChange;
            arg = null;
            asyncOp = null;
            lockParam = null;
            scenes.Clear();
            scenes = null;
        }

        public void LoadEmptyScene()
        {
            if (EmptyScene==null)
            {
                EmptyScene = UnityEngine.SceneManagement.SceneManager.CreateScene(EmptySceneName);
            }
            LoadSync(EmptySceneName, LoadSceneMode.Additive);
        }
        public void LoadSync(string sceneName, LoadSceneMode mode)
        {
            UnityEngine.SceneManagement.SceneManager.LoadScene(sceneName, mode);
        }
        public void LoadAsyncWithEmpty(string sceneName, LoadSceneMode mode)
        {
            LoadEmptyScene();
            LoadAsync(sceneName, mode);
        }

        public void LoadAsync(string sceneName, LoadSceneMode mode)
        {
            using (LockWait wait = new LockWait(ref lockParam))
            {
                asyncOp = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(sceneName, mode);
                asyncOp.allowSceneActivation = false;
                arg.LoadMode = mode;
                arg.LoadedScene = GetSceneByName(sceneName);
                arg.EveType = SceneEventType.StartLoadAsync;
                ObserveManager.Instance.Notify<SceneManager>(arg, 0);
                arg.EveType = SceneEventType.LoadProgress;
                while (!asyncOp.isDone)
                {
                    arg.Progress = asyncOp.progress;
                    ObserveManager.Instance.Notify<SceneManager>(arg, 0);
                }
                arg.Progress = 1;
                arg.EveType = SceneEventType.LoadComplete;
                ObserveManager.Instance.Notify<SceneManager>(arg, 0);
                asyncOp.allowSceneActivation = true;
            }
        }

        public void UnLoad(Scene scene)
        {
            using (LockWait wait = new LockWait(ref lockParam))
            {
                asyncOp = UnityEngine.SceneManagement.SceneManager.UnloadSceneAsync(scene);
                arg.EveType = SceneEventType.StartUnLoadAsync;
                ObserveManager.Instance.Notify<SceneManager>(arg, 0);
                arg.EveType = SceneEventType.UnLoadProgress;
                while (!asyncOp.isDone)
                {
                    arg.Progress = asyncOp.progress;
                    ObserveManager.Instance.Notify<SceneManager>(arg, 0);
                }
                arg.Progress = 1;
                arg.EveType = SceneEventType.UnLoadComplete;
                ObserveManager.Instance.Notify<SceneManager>(arg, 0);
            }
        }
        public void UnLoad(string sceneName)
        {
            UnLoad(GetSceneByName(sceneName));
        }
        public Scene[] GetLoadedScenes()
        {
            return scenes.ToArray();
        }
        public bool HaveLoad(Scene scene)
        {
            return scenes.Contains(scene);
        }
        public bool HaveLoad(string sceneName)
        {
            return HaveLoad(GetSceneByName(sceneName));
        }
        public bool SetActive(Scene scene)
        {
           return  UnityEngine.SceneManagement.SceneManager.SetActiveScene(scene);
        }
        public bool SetActive(string sceneName)
        {
            return UnityEngine.SceneManagement.SceneManager.SetActiveScene(GetSceneByName(sceneName));
        }

        public Scene GetSceneByName(string name)
        {
            return UnityEngine.SceneManagement.SceneManager.GetSceneByName(name);
        }
        public Scene GetSceneByPath(string path)
        {
            return UnityEngine.SceneManagement.SceneManager.GetSceneByPath(path);
        }
        public Scene GetSceneByIndex(int index)
        {
            return UnityEngine.SceneManagement.SceneManager.GetSceneByBuildIndex(index);
        }

        public int GetBuildIndexByScenePath(string path)
        {
            return SceneUtility.GetBuildIndexByScenePath(path);
        }
        public string GetScenePathByBuildIndex(int index)
        {
            return SceneUtility.GetScenePathByBuildIndex(index);
        }
    }
}
